home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 37 / Amiga Format CD37 (1999-02-16)(Future Publishing)(GB)(Track 1 of 3)[!][issue 1999-03].iso / -screenplay- / shareware / invasionforce / source / game_play2.c < prev    next >
C/C++ Source or Header  |  1999-01-09  |  74KB  |  2,214 lines

  1. /*
  2.    game_play2.c -- game play module for Empire II
  3.  
  4.    This module handles game play: loading and saving games, getting orders
  5.    from the user, moving units, handling combat, and presenting various
  6.    menus for branching to the status reports, message system, etc.
  7.  
  8.    This source code is free.  You may make as many copies as you like.
  9. */
  10.  
  11. #include "global.h"
  12. #include <proto/layers.h>
  13.  
  14. #define CLEAR_WINDOW() SetRast(rast_port,0);zero_scrollers();display=0;
  15.  
  16. #define EXIT_GAME 1     // kluge values for flow control
  17. #define GAME_OVER 2
  18. #define UNIT_DONE 3     // I mainly use these to break out of nested functions
  19. #define UNIT_LOST 4
  20. #define GO_SURVEY 5
  21. #define EXIT_SURVEY   6
  22. #define GO_MOVEMENT   7
  23. #define END_TURN      8
  24. #define GAME_RESTORED 9
  25. #define GO_PRODUCTION 10
  26. #define CE_RESTART -17
  27.  
  28. #define INVISIBLE 0
  29. #define VISIBLE 1       // flag values, purely for more readable code
  30.  
  31.  
  32.  
  33. void survey_mode(current_unit)
  34. struct Unit **current_unit;
  35. {
  36.    /*
  37.       This is similar in many respects to movement_mode()
  38.       As we enter this function, the global values cursx and cursy should
  39.       already have been set, indicating the current cursor location.
  40.       However, if cursx,cursy is not visible, it is permissible to reset
  41.       the cursor to someplace on the screen rather than scroll to it.
  42.    */
  43.    struct IntuiMessage *message; // the message the IDCMP sends us
  44.    unsigned int ticks = 1;
  45.    BOOL blink_on = TRUE;
  46.  
  47.    // useful for interpreting IDCMP messages
  48.    UWORD code;
  49.    ULONG class;
  50.    APTR object;
  51.  
  52.    if (display==FALSE) {
  53.       set_display_offsets(cursx,cursy);
  54.       GP_draw_map();
  55.    } else if (need_to_scrollP(cursx,cursy)) {
  56.       /*
  57.       int ox=xoffs, oy=yoffs;
  58.  
  59.       set_display_offsets(cursx,cursy);
  60.       GP_smart_scroll(ox,oy);
  61.       */
  62.       cursx = xoffs+disp_wd/2;
  63.       cursy = yoffs+disp_ht/2;
  64.    FI
  65.  
  66.    save_hex_graphics(cursx,cursy,0);
  67.    hex_status_bar(cursx,cursy);
  68.  
  69.    // activate IDCMP event input
  70.    ModifyIDCMP(map_window,IDCMP_PLAYGAME);
  71.  
  72.    // attach the menu to my window
  73.    SetMenuStrip(map_window,vey_menu_strip);
  74.  
  75.    // enable menus
  76.    OnMenu(map_window,FULLMENUNUM(0,-1,0));
  77.    OnMenu(map_window,FULLMENUNUM(1,-1,0));
  78.    OnMenu(map_window,FULLMENUNUM(2,-1,0));
  79.    OnMenu(map_window,FULLMENUNUM(3,-1,0));
  80.    OnMenu(map_window,FULLMENUNUM(4,-1,0));
  81.  
  82.    control_flag = 0;    // flag lets me know when player is done
  83.  
  84.    /*
  85.       This is another important user input loop where
  86.       I let the user scroll around, look at units, cities,
  87.       select various options from the drop-down menus, etc.
  88.    */
  89.  
  90.    while (TRUE) {
  91.       WaitPort(map_window->UserPort);
  92.       while (message = GT_GetIMsg(map_window->UserPort)) {
  93.          ClearMenuStrip(map_window);
  94.          code = message->Code;  // MENUNUM
  95.          object = message->IAddress;  // Gadget
  96.          class = message->Class;
  97.          GT_ReplyIMsg(message);
  98.          if ( class == MENUPICK ) { // MenuItems
  99.             switch (MENUNUM(code)) {
  100.                case 0:  // Project menu
  101.                   switch (ITEMNUM(code)) {
  102.                      case 0:  // save game
  103.                         {
  104.                            char pan[216];
  105.                            build_pan(pan,game_filepath,game_filename);
  106.                            save_game(pan);
  107.                         }
  108.                         break;
  109.                      case 1:  // save as...
  110.                         (void)rt_loadsave_game(TRUE);
  111.                         break;
  112.                      case 3:  // exit game
  113.                         if (alert(map_window,"Exit this game.","Are you sure you want to abandon this game now?","Exit|Cancel")) {
  114.                            control_flag = EXIT_GAME;
  115.                            goto exit_surveymode;
  116.                         FI
  117.                         break;
  118.                      case 4:  // quit program
  119.                         if (alert(map_window,"Exit Invasion Force.","You have a game in progress.\nAre you sure you want to leave Invasion Force now?","Exit|Cancel"))
  120.                            clean_exit(0,NULL);
  121.                         break;
  122.                      default:
  123.                         class = IDCMP_VANILLAKEY;
  124.                         code = 200;
  125.                   };
  126.                   break;
  127.                case 1:  // Reports menu
  128.                   switch (ITEMNUM(code)) {
  129.                      case 0:  // World Map
  130.                         GP_world_view();
  131.                         break;
  132.                      case 1:  // Status report
  133.                         status_report(player);
  134.                         break;
  135.                      case 2:  // Combat Report
  136.                         show_combat_report(FALSE);
  137.                         break;
  138.                      case 3:  // Hex Info
  139.                         class = IDCMP_VANILLAKEY;
  140.                         code = '\r';
  141.                         break;
  142.                      case 4:  // Production Map
  143.                         control_flag = GO_PRODUCTION;
  144.                         goto exit_surveymode;
  145.                         break;
  146.                      default:
  147.                         class = IDCMP_VANILLAKEY;
  148.                         code = 200;
  149.                   }
  150.                   break;
  151.                case 2:  // Orders menu
  152.                   switch (ITEMNUM(code)) {
  153.                      case 0:  // Clear Orders
  154.                         class = IDCMP_VANILLAKEY;
  155.                         code = 'o';
  156.                         break;
  157.                      case 1:  // Cycle Stack
  158.                         class = IDCMP_VANILLAKEY;
  159.                         code = '5';
  160.                         break;
  161.                      case 2:  // Activate
  162.                         class = IDCMP_VANILLAKEY;
  163.                         code = 'a';
  164.                         break;
  165.                      default:
  166.                         class = IDCMP_VANILLAKEY;
  167.                         code = 200;
  168.                   }
  169.                   break;
  170.                case 3:  // Commands menu
  171.                   switch (ITEMNUM(code)) {
  172.                      case 0:  // Move Mode
  173.                         class = IDCMP_VANILLAKEY;
  174.                         code = 'm';
  175.                         break;
  176.                      case 3:  // Center Screen
  177.                         class = IDCMP_VANILLAKEY;
  178.                         code = 'c';
  179.                         break;
  180.                      default:
  181.                         class = IDCMP_VANILLAKEY;
  182.                         code = 200;
  183.                   }
  184.                   break;
  185.                case 4:  // Other menu
  186.                   switch (ITEMNUM(code)) {
  187.                      case 0:  // Prefs
  188.                         player_preferences();
  189.                         break;
  190.                      default:
  191.                         code = 200;
  192.                         class = IDCMP_VANILLAKEY;
  193.                   }
  194.             }  // end of drop-down menus
  195.          FI
  196.          // following are key commands available in survey mode
  197.          if (class==IDCMP_VANILLAKEY) {
  198.             switch (code) {
  199.                int direction;
  200.                case 'q':
  201.                   // Magic Q function - change the level of AI displays
  202.                   ToggleAIDataFlag();
  203.                   break;
  204.                case '\r':
  205.                case ' ':
  206.                   if (blink_on) {   // remove the cursor if it's visible
  207.                      blink_on=FALSE;
  208.                      restore_hex_graphics(cursx,cursy,0);
  209.                   FI
  210.                   // survey_hex(cursx,cursy);
  211.                   sector_survey(cursx,cursy);
  212.  
  213.                   break;
  214.                case 'a':   // activate unit at this location
  215.                   {
  216.                      struct Unit *unit=(struct Unit *)unit_list.mlh_Head;
  217.  
  218.                      for (; unit->unode.mln_Succ; unit=(struct Unit *)unit->unode.mln_Succ)
  219.                         if (unit->col==cursx && unit->row==cursy && unit->owner==player)
  220.                            if (Bool(unit_readiness(unit)&UNIT_READY) && unit->ship==NULL) {
  221.                               clear_orders(unit);
  222.                               *current_unit = unit;
  223.                               control_flag = EXIT_SURVEY;
  224.                               break;
  225.                            FI
  226.                   }
  227.                   break;
  228.                case 'c':
  229.                   {  // center the display on the current hex cell
  230.                      int ox = xoffs, oy = yoffs;
  231.  
  232.                      restore_hex_graphics(cursx,cursy,0);
  233.                      set_display_offsets(cursx,cursy);
  234.                      GP_smart_scroll(ox,oy);
  235.                      if (blink_on)
  236.                         plot_cursor(cursx,cursy);
  237.                   }
  238.                   break;
  239.                case 'm':   // to movement mode
  240.                   control_flag = EXIT_SURVEY;
  241.                   break;
  242.                case 'o':   // clear orders
  243.                   {  // go down the list of units for ones in this hex
  244.                      struct Unit *unit=(struct Unit *)unit_list.mlh_Head;
  245.  
  246.                      for (;unit->unode.mln_Succ;unit=(struct Unit *)unit->unode.mln_Succ)
  247.                         if (unit->col==cursx && unit->row==cursy && unit->owner==player)
  248.                            clear_orders(unit);
  249.                      /*** TLB ***/
  250.                      // update both the hex display and the window title
  251.                      blink_on=FALSE;
  252.                      explore_hex(player,cursx,cursy,VISIBLE,TRUE);
  253.                      save_hex_graphics(cursx,cursy,0);
  254.                      hex_status_bar(cursx,cursy);
  255.  
  256.                    }
  257.                  break;
  258.                case '5':   // shuffle stack
  259.                   {  // search for top unit in this hex
  260.                      struct Unit *topunit=(struct Unit *)unit_list.mlh_Head, *newunit;
  261.  
  262.                      for (;topunit->unode.mln_Succ;topunit=(struct Unit *)topunit->unode.mln_Succ)
  263.                         if (topunit->col==cursx && topunit->row==cursy && topunit->owner==player) {
  264.                            newunit = shuffle_units(topunit,TRUE);
  265.                            if (newunit!=topunit) {
  266.                               // update display to match
  267.                               explore_hex(player,cursx,cursy,VISIBLE,TRUE);
  268.                               save_hex_graphics(cursx,cursy,0);
  269.                               hex_status_bar(cursx,cursy);
  270.                               break;
  271.                            FI
  272.                         FI
  273.                      break;
  274.                   }
  275.                // following is a sort of movement table, translates
  276.                // keypad numbers into movement directions
  277.                case '6':
  278.                   direction = EAST;
  279.                   goto sm_moving;
  280.                case '4':
  281.                   direction = WEST;
  282.                   goto sm_moving;
  283.                case '7':
  284.                   direction = NORTHWEST;
  285.                   goto sm_moving;
  286.                case '9':
  287.                   direction = NORTHEAST;
  288.                   goto sm_moving;
  289.                case '1':
  290.                   direction = SOUTHWEST;
  291.                   goto sm_moving;
  292.                case '3':
  293.                   direction = SOUTHEAST;
  294.                sm_moving:
  295.                   restore_hex_graphics(cursx,cursy,0);
  296.                   move_cursor_dir(direction);
  297.                   if (need_to_scrollP(cursx,cursy)) {
  298.                      int ox = xoffs, oy = yoffs;
  299.  
  300.                      set_display_offsets(cursx,cursy);
  301.                      GP_smart_scroll(ox,oy);
  302.                   FI
  303.                   save_hex_graphics(cursx,cursy,0);
  304.                   if (blink_on)
  305.                      plot_cursor(cursx,cursy);
  306.                   hex_status_bar(cursx,cursy);
  307.                   break;
  308.                case 200:
  309.                   (void)rtEZRequestTags("That function is not yot implemented.","Drat!",NULL,NULL,
  310.                      RT_DEFAULT,TAG_END);
  311.                   break;
  312.                default:
  313.                   (void)rtEZRequestTags("That key has no defined function.\nPlease try one of the many other keys.",
  314.                      "Okay",NULL,NULL,
  315.                      RT_Window,        map_window,
  316.                      RT_ReqPos,        REQPOS_CENTERSCR,
  317.                      RT_LockWindow,    TRUE,
  318.                      RTEZ_Flags,       EZREQF_CENTERTEXT,
  319.                      TAG_DONE );
  320.             } // end switch
  321.          FI
  322.          if (class==IDCMP_MOUSEBUTTONS && code==SELECTDOWN) {
  323.             int x, y;
  324.  
  325.             abs_to_log(message->MouseX,message->MouseY,&x,&y);
  326.             restore_hex_graphics(cursx,cursy,0);
  327.             cursx = x;     cursy = y;
  328.             if (need_to_scrollP(cursx,cursy)) {
  329.                int ox = xoffs, oy = yoffs;
  330.  
  331.                set_display_offsets(cursx,cursy);
  332.                GP_smart_scroll(ox,oy);
  333.             FI
  334.             hex_status_bar(cursx,cursy);
  335.             save_hex_graphics(cursx,cursy,0);
  336.             if (blink_on)
  337.                plot_cursor(cursx,cursy);
  338.          FI
  339.          if (class==IDCMP_GADGETUP) {
  340.             int ox = xoffs, oy = yoffs;
  341.  
  342.             restore_hex_graphics(cursx,cursy,0);
  343.             if (scrolly(object,code)) {
  344.                GP_smart_scroll(ox,oy);
  345.             FI
  346.             if (blink_on)
  347.                plot_cursor(cursx,cursy);
  348.          FI
  349.          if (class == IDCMP_INTUITICKS)  // intuiticks control blinking
  350.             if ((++ticks % 5) == 0) {
  351.                blink_on = !blink_on;
  352.                if (blink_on)
  353.                   plot_cursor(cursx,cursy);
  354.                else
  355.                   restore_hex_graphics(cursx,cursy,0);
  356.             FI
  357.          ResetMenuStrip(map_window,vey_menu_strip);
  358.  
  359.          if (control_flag==EXIT_SURVEY) {
  360.             restore_hex_graphics(cursx,cursy,0);
  361.             goto exit_surveymode;
  362.          FI
  363.       OD
  364.    OD
  365.  exit_surveymode:
  366.    // deactivate IDCMP event input
  367.    ModifyIDCMP(map_window,NULL);
  368.  
  369.    // return to my "generic" title bar, so no outdated information is
  370.    // seen when it shouldn't be -- especially by the next player!
  371.    clear_movebar();
  372.    SetWindowTitles(map_window,"Game in progress...",(UBYTE *)~0);
  373. }
  374.  
  375.  
  376. void production_mode()
  377. {
  378.    /*
  379.       This is similar in many respects to survey_mode(), but much simpler.
  380.       As we enter this function, the global values cursx and cursy should
  381.       already have been set, indicating the current cursor location.
  382.       However, if cursx,cursy is not visible, it is permissible to reset
  383.       the cursor to someplace on the screen rather than scroll to it.
  384.    */
  385.    struct IntuiMessage *message; // the message the IDCMP sends us
  386.    unsigned int ticks = 1;
  387.    BOOL blink_on = TRUE;
  388.  
  389.    // useful for interpreting IDCMP messages
  390.    UWORD code;
  391.    ULONG class;
  392.    APTR object;
  393.  
  394.    if (need_to_scrollP(cursx,cursy)) {
  395.       cursx = xoffs+disp_wd/2;
  396.       cursy = yoffs+disp_ht/2;
  397.    FI
  398.    PM_draw_map();
  399.  
  400.    save_hex_graphics(cursx,cursy,0);
  401.    hex_status_bar(cursx,cursy);
  402.  
  403.    // activate IDCMP event input
  404.    ModifyIDCMP(map_window,IDCMP_PLAYGAME);
  405.  
  406.    // attach the menu to my window
  407.    SetMenuStrip(map_window,prod_menu_strip);
  408.  
  409.    // enable menu
  410.    OnMenu(map_window,FULLMENUNUM(0,-1,0));
  411.    OnMenu(map_window,FULLMENUNUM(1,-1,0));
  412.    OnMenu(map_window,FULLMENUNUM(2,-1,0));
  413.    OnMenu(map_window,FULLMENUNUM(3,-1,0));
  414.  
  415.    control_flag = 0;    // flag lets me know when player is done
  416.  
  417.    /*
  418.       I let the user scroll around, look at cities, reset their
  419.       production, and select the return to survey or movement mode.
  420.    */
  421.    while (TRUE) {
  422.       WaitPort(map_window->UserPort);
  423.       while (message = GT_GetIMsg(map_window->UserPort)) {
  424.          ClearMenuStrip(map_window);
  425.          code = message->Code;  // MENUNUM
  426.          object = message->IAddress;  // Gadget
  427.          class = message->Class;
  428.          GT_ReplyIMsg(message);
  429.          if (class==MENUPICK) { // MenuItems
  430.             switch (MENUNUM(code)) {
  431.                case 0:  // Project menu
  432.                   switch (ITEMNUM(code)) {
  433.                      case 0:  // save game
  434.                         {
  435.                            char pan[216];
  436.                            build_pan(pan,game_filepath,game_filename);
  437.                            save_game(pan);
  438.                         }
  439.                         break;
  440.                      case 1:  // save as...
  441.                         (void)rt_loadsave_game(TRUE);
  442.                         break;
  443.                      case 3:  // exit game
  444.                         if (alert(map_window,"Exit this game.","Are you sure you want to abandon this game now?","Exit|Cancel")) {
  445.                            control_flag = EXIT_GAME;
  446.                            goto exit_productionmode;
  447.                         FI
  448.                         break;
  449.                      case 4:  // quit program
  450.                         if (alert(map_window,"Exit Invasion Force.","You have a game in progress.\nAre you sure you want to leave Invasion Force now?","Exit|Cancel"))
  451.                            clean_exit(0,NULL);
  452.                         break;
  453.                      default:
  454.                         code = 200;
  455.                         class = IDCMP_VANILLAKEY;
  456.                   };
  457.                   break;
  458.                case 1:  // Reports menu
  459.                   switch (ITEMNUM(code)) {
  460.                      case 0:  // World Map
  461.                         GP_world_view();
  462.                         break;
  463.                      case 1:  // Status report
  464.                         status_report(player);
  465.                         break;
  466.                      default:
  467.                         code = 200;
  468.                         class = IDCMP_VANILLAKEY;
  469.                   }
  470.                   break;
  471.                case 2:  // Commands menu
  472.                   switch (ITEMNUM(code)) {
  473.                      case 0:  // Examine City
  474.                         code = '\r';
  475.                         class = IDCMP_VANILLAKEY;
  476.                         break;
  477.                      case 1:  // Movement Mode
  478.                         code = 'm';
  479.                         class = IDCMP_VANILLAKEY;
  480.                         break;
  481.                      case 2:  // Survey Mode
  482.                         code = 'v';
  483.                         class = IDCMP_VANILLAKEY;
  484.                         break;
  485.                      case 3:  // Center Screen
  486.                         code = 'c';
  487.                         class = IDCMP_VANILLAKEY;
  488.                         break;
  489.                      default:
  490.                         code = 200;
  491.                         class = IDCMP_VANILLAKEY;
  492.                   }
  493.                   break;
  494.                case 3:  // Other menu
  495.                   switch (ITEMNUM(code)) {
  496.                      case 0:  // Prefs
  497.                         player_preferences();
  498.                         break;
  499.                      default:
  500.                         code = 200;
  501.                         class = IDCMP_VANILLAKEY;
  502.                   }
  503.             }  // end of drop-down menus
  504.          FI
  505.          // following are key commands available in survey mode
  506.          if (class==IDCMP_VANILLAKEY) {
  507.             switch (code) {
  508.                struct City *metro;
  509.                int direction;
  510.                case '\r':
  511.                case ' ':
  512.                   if (metro = city_hereP(cursx,cursy)) {
  513.                      if (metro->owner==player) {
  514.                         if (blink_on) {   // remove the cursor if it's visible
  515.                            blink_on=FALSE;
  516.                            restore_hex_graphics(cursx,cursy,0);
  517.                         FI
  518.                         examine_city(metro);
  519.                         PM_update_hex(metro->col,metro->row);
  520.                         save_hex_graphics(metro->col,metro->row,0);
  521.                      } else
  522.                         (void)alert(map_window,"Information...","That city doesn't belong to you!","Cancel");
  523.                   } else
  524.                      playSound(DONK_SOUND,PLAYER.snd_vol);
  525.                   break;
  526.                case 'c':
  527.                   {  // center the display on the current hex cell
  528.                      int ox = xoffs, oy = yoffs;
  529.  
  530.                      restore_hex_graphics(cursx,cursy,0);
  531.                      set_display_offsets(cursx,cursy);
  532.                      PM_smart_scroll(ox,oy);
  533.                      if (blink_on)
  534.                         plot_cursor(cursx,cursy);
  535.                   }
  536.                   break;
  537.                case 'm':   // to movement mode
  538.                   control_flag = GO_MOVEMENT;
  539.                   goto exit_productionmode;
  540.                case 'v':   // to survey mode
  541.                   control_flag = GO_SURVEY;
  542.                   goto exit_productionmode;
  543.                // following is a sort of movement table, translates
  544.                // keypad numbers into movement directions
  545.                case '6':
  546.                   direction = EAST;
  547.                   goto pm_moving;
  548.                case '4':
  549.                   direction = WEST;
  550.                   goto pm_moving;
  551.                case '7':
  552.                   direction = NORTHWEST;
  553.                   goto pm_moving;
  554.                case '9':
  555.                   direction = NORTHEAST;
  556.                   goto pm_moving;
  557.                case '1':
  558.                   direction = SOUTHWEST;
  559.                   goto pm_moving;
  560.                case '3':
  561.                   direction = SOUTHEAST;
  562.                pm_moving:
  563.                   restore_hex_graphics(cursx,cursy,0);
  564.                   move_cursor_dir(direction);
  565.                   if (need_to_scrollP(cursx,cursy)) {
  566.                      int ox = xoffs, oy = yoffs;
  567.  
  568.                      set_display_offsets(cursx,cursy);
  569.                      PM_smart_scroll(ox,oy);
  570.                   }
  571.                   save_hex_graphics(cursx,cursy,0);
  572.                   if (blink_on)
  573.                      plot_cursor(cursx,cursy);
  574.                   hex_status_bar(cursx,cursy);
  575.                   break;
  576.                case 200:   // undefined drop-down menu function
  577.                   (void)rtEZRequestTags("That function is not yot implemented.","Drat!",NULL,NULL,
  578.                      RT_DEFAULT,TAG_END);
  579.                   break;
  580.                default:
  581.                   (void)rtEZRequestTags("That key has no defined function.\nPlease try one of the many other keys.",
  582.                      "Okay",NULL,NULL,
  583.                      RT_Window,        map_window,
  584.                      RT_ReqPos,        REQPOS_CENTERSCR,
  585.                      RT_LockWindow,    TRUE,
  586.                      RTEZ_Flags,       EZREQF_CENTERTEXT,
  587.                      TAG_DONE );
  588.             } // end switch
  589.          FI
  590.          if (class==IDCMP_MOUSEBUTTONS && code==SELECTDOWN) {
  591.             int x, y;
  592.  
  593.             abs_to_log(message->MouseX,message->MouseY,&x,&y);
  594.             restore_hex_graphics(cursx,cursy,0);
  595.             if (cursx==x && cursy==y) {
  596.                struct City *metro=city_hereP(x,y);
  597.  
  598.                if (metro)
  599.                   if (metro->owner==player) {
  600.                      examine_city(metro);
  601.                      PM_update_hex(cursx,cursy);
  602.                      save_hex_graphics(cursx,cursy,0);
  603.                   FI
  604.             } else {
  605.                cursx = x;     cursy = y;
  606.                if (need_to_scrollP(cursx,cursy)) {
  607.                   int ox = xoffs, oy = yoffs;
  608.  
  609.                   set_display_offsets(cursx,cursy);
  610.                   PM_smart_scroll(ox,oy);
  611.                FI
  612.                hex_status_bar(cursx,cursy);
  613.                save_hex_graphics(cursx,cursy,0);
  614.                if (blink_on)
  615.                   plot_cursor(cursx,cursy);
  616.             FI
  617.          FI
  618.          if (class==IDCMP_GADGETUP) {
  619.             int ox = xoffs, oy = yoffs;
  620.  
  621.             restore_hex_graphics(cursx,cursy,0);
  622.             if (scrolly(object,code)) {
  623.                PM_smart_scroll(ox,oy);
  624.             FI
  625.             if (blink_on)
  626.                plot_cursor(cursx,cursy);
  627.          FI
  628.          if (class == IDCMP_INTUITICKS)  // intuiticks control blinking
  629.             if ((++ticks % 5) == 0) {
  630.                blink_on = !blink_on;
  631.                if (blink_on)
  632.                   plot_cursor(cursx,cursy);
  633.                else
  634.                   restore_hex_graphics(cursx,cursy,0);
  635.             FI
  636.          ResetMenuStrip(map_window,prod_menu_strip);
  637.       OD
  638.    OD
  639.  exit_productionmode:
  640.    // deactivate IDCMP event input
  641.    ModifyIDCMP(map_window,NULL);
  642.  
  643.    // return to my "generic" title bar
  644.    clear_movebar();
  645.    SetWindowTitles(map_window,"Game in progress...",(UBYTE *)~0);
  646. }
  647.  
  648.  
  649. // give user information about the specified hex
  650. // this is a simple function which will be replaced later
  651. // by something much more advanced
  652.  
  653. void survey_hex(col,row)
  654. {
  655.    struct MapIcon *icon;
  656.    struct Unit *unit;
  657.    struct City *metro;
  658.    int terrain, controller=-1;
  659.  
  660.    // get my terrain and see if it's explored
  661.    terrain = get(PLAYER.map,col,row);
  662.  
  663.    if (terrain==HEX_UNEXPLORED) {
  664.       alert(map_window,NULL,"That area is unexplored.","Okay");
  665.       return;
  666.    } else {
  667.       sprintf(foo,"The terrain is type \"%s\".",terrain_name_table[terrain]);
  668.       alert(map_window,NULL,foo,"Okay");
  669.    FI
  670.  
  671.    // see if there is a city, and who it belongs to
  672.    if (metro=city_hereP(col,row)) {
  673.       if (metro->owner==player) {
  674.          controller = player;
  675.          // do this so the user can see which city he is looking at
  676.          save_hex_graphics(metro->col,metro->row,0);    // blit the background to a safe place
  677.          plot_mapobject(metro->col,metro->row,MAP_MARKER);
  678.          examine_city(metro);
  679.          // unmark the city on screen
  680.          restore_hex_graphics(metro->col,metro->row,0);
  681.       } else {
  682.          controller = metro->owner;
  683.          sprintf(foo,"CITY: %s\nOWNER: %s\nPRODUCING: ",
  684.             metro->name, roster[controller].name);
  685.          if (metro->owner==0)
  686.             strcat(foo,"none");
  687.          else
  688.             if (metro->recon[player]==CITY)
  689.                strcat(foo,"unknown");
  690.             else
  691.                strcat(foo,wishbook[metro->recon[player]].name);
  692.          alert(map_window,NULL,foo,"Okay");
  693.       FI
  694.    FI
  695.  
  696.    // determine who controls this hex, if we don't already know
  697.    if (controller<0)
  698.       // see if the current player has any units in this hex
  699.       for (unit=(struct Unit *)unit_list.mlh_Head; unit->unode.mln_Succ; unit=(struct Unit *)unit->unode.mln_Succ)
  700.          if (unit->owner==player) {
  701.             controller = player;
  702.             break;
  703.          }
  704.       // see if there are any enemy units displayed on this map hex
  705.       for (icon = (struct MapIcon *)PLAYER.icons.mlh_Head; icon->inode.mln_Succ; icon=(struct MapIcon *)icon->inode.mln_Succ) {
  706.          if (icon->col==col && icon->row==row) {
  707.             controller = icon->owner;
  708.             break;
  709.          FI
  710.  
  711.       // if we still haven't found anything...
  712.       if (controller<0)
  713.          controller=0;
  714.    FI
  715.  
  716.    // if the current player controls it, show all his units here
  717.    if (controller==player) {
  718.       for (unit=(struct Unit *)unit_list.mlh_Head; unit->unode.mln_Succ; unit=(struct Unit *)unit->unode.mln_Succ)
  719.          if (unit->owner==player && unit->col==col && unit->row==row) {
  720.             sprintf(foo,"UNIT TYPE: %s\nNAME: %s\nOWNER: %s\nROW: %ld, COL: %ld",
  721.                wishbook[unit->type].name,unit->name,PLAYER.name,row,col);
  722.             alert(map_window,NULL,foo,"Okay");
  723.          FI
  724.    FI
  725.  
  726.    // if another player controls it, review all the icons
  727.    // {not yet implemented}
  728. }
  729.  
  730.  
  731. /*
  732.    The following function determines which of a player's pieces should be moved
  733.    next (or handled by the order manager) by default.  It will use several
  734.    factors to determine this, such as position of various units on the map, their
  735.    order in the list, etc.  It returns a pointer to the unit it settled on.
  736.    If a suitable unit is not found, the function returns NULL.
  737. */
  738.  
  739. struct Unit *choose_default_unit(exclude)
  740. struct Unit *exclude;
  741. {
  742.    struct Unit *unit, *chosen_unit=NULL;
  743.    BOOL unit_found=FALSE;
  744.    int ready;
  745.    /*
  746.       Go down the list of active units...
  747.  
  748.       This is a tricky proposition, since we need to process all the units
  749.       belonging to the current player, but he may change the order in which
  750.       they are moved.  Units can also be destroyed!  So, we can't just blindly
  751.       go down the list.
  752.  
  753.       A better approach is to build a loop where each iteration seeks out
  754.       the first suitable unit and processes it.  It continues until it
  755.       fails to find any units that need processing.
  756.  
  757.       I use an extra loop to take screen position into account, attempting to
  758.       exhaust the units on the visible part of the map first, to reduce the
  759.       amount of scrolling needed.
  760.  
  761.       I will try to avoid using the "exclude" unit, if one has been named.
  762.       This is so I can implement the "next unit" command and skip over a
  763.       specified unit that the user wants to deal with later.
  764.    */
  765.    // first loop searches for units visible on the current map display
  766.    for (unit=(struct Unit *)unit_list.mlh_Head;unit->unode.mln_Succ;unit=(struct Unit *)unit->unode.mln_Succ)
  767.       if (easily_visibleP(unit->col,unit->row) && unit!=exclude) {
  768.          ready = unit_readiness(unit);
  769.          if (((ready&UNIT_READY)!=0) && ((ready&UNIT_PROCESSED)==0)) {
  770.             unit_found = TRUE;
  771.             break;
  772.          FI
  773.       FI
  774.  
  775.    if (!unit_found)  // do second loop only if first one turned up nothing
  776.       for (unit=(struct Unit *)unit_list.mlh_Head;unit->unode.mln_Succ;unit=(struct Unit *)unit->unode.mln_Succ)
  777.          if (unit!=exclude) {    // skip the excluded unit
  778.             ready = unit_readiness(unit);
  779.             if (((ready&UNIT_READY)!=0) && ((ready&UNIT_PROCESSED)==0)) {
  780.                unit_found = TRUE;
  781.                break;
  782.             FI
  783.          FI
  784.  
  785.    if (unit_found)  // we've found our unit!
  786.       chosen_unit = unit;
  787.    if (exclude!=NULL && unit_found==FALSE)
  788.       chosen_unit = choose_default_unit(NULL);
  789.    return chosen_unit;
  790. }
  791.  
  792.  
  793. /*
  794.    unit_readiness() can return four different bit flags for the status of the
  795.    unit specified: UNIT_UNREADY, meaning the unit is invalid (NULL), does
  796.    not belong to the current player, or has expended its movement this turn,
  797.    or cannot move for some other reason; UNIT_READY, meaning the unit is
  798.    ready to move and awaiting orders; UNIT_ENGAGED, meaning the unit has
  799.    pending orders such as sentry duty, loading units, or moving to a specified
  800.    location (such activities can be interrupted by the player); UNIT_PROCESSED,
  801.    meaning it has orders but they have already been processed for this turn.
  802.    These flags are not mutually exclusive: a unit could be both UNIT_UNREADY
  803.    and UNIT_PROCESSED, for example.
  804. */
  805.  
  806. int unit_readiness(unit)
  807. struct Unit *unit;
  808. {
  809.    int status = UNIT_READY;
  810.  
  811.    if (unit==NULL)
  812.       return UNIT_UNREADY;
  813.  
  814.    if (unit->owner!=player || unit->move<10)
  815.       status = UNIT_UNREADY;
  816.  
  817.    if (unit->orders) {
  818.       if (unit->orders->processed)
  819.          status |= UNIT_PROCESSED;
  820.       else
  821.          status |= UNIT_ENGAGED;
  822.    FI
  823.    return status;
  824. }
  825.  
  826.  
  827. /*
  828.    The order_manager() function handles units that have standing orders to
  829.    be executed.  It sorts them by order type and carries out the appropriate
  830.    functions for them.
  831. */
  832.  
  833. void order_manager(unit)
  834. struct Unit *unit;
  835. {
  836.    short type=unit->orders->type;
  837.  
  838.    // If there is no map display, this is the right time to create one.
  839.    if (type!=ORDER_SENTRY)
  840.       create_player_display(unit->col,unit->row);
  841.  
  842.    if (PLAYER.show&SHOW_GRP && type!=ORDER_SENTRY)
  843.       if (need_to_scrollP(unit->col,unit->row)) {
  844.          int ox=xoffs, oy=yoffs;
  845.  
  846.          set_display_offsets(unit->col,unit->row);
  847.          GP_smart_scroll(ox,oy);
  848.       }
  849.  
  850.    unit->orders->processed = TRUE;
  851.    if (type==ORDER_LOAD)
  852.       load_ship(unit);
  853.    if (type==ORDER_GOTO)
  854.       do_goto(unit);
  855.    if (type==ORDER_AIRBASE)
  856.       build_airbase(unit);
  857. }
  858.  
  859.  
  860. void build_airbase(unit)
  861. struct Unit *unit;
  862. {
  863.    // The purpose being to transform "unit" into an airbase, if possible.
  864.  
  865.    int time;   // controlling how long the construction will take
  866.  
  867.    /*
  868.       If the unit already has orders for something other than building an
  869.       airbase, we should clear them.  Actually, build_airbase() shouldn't
  870.       ever be called in a situation like that, but it doesn't hurt to make
  871.       sure and make it robust.
  872.    */
  873.    if (unit->orders)
  874.       if (unit->orders->type!=ORDER_AIRBASE) {
  875.          FreeVec(unit->orders);
  876.          unit->orders = NULL;
  877.       FI
  878.  
  879.    /*
  880.       Terrain!
  881.       An airbase can't be built in a city, and only on certain types
  882.       of terrain.  The type of terrain will also influence the amount of
  883.       time required to build it.
  884.    */
  885.    {
  886.       int terrain = get(t_grid,unit->col,unit->row);
  887.       int invalid;
  888.       struct Unit *scan;
  889.  
  890.       time = 1;
  891.       switch (terrain) {
  892.          case HEX_JUNGLE:
  893.          case HEX_HILLS:
  894.             time++;
  895.          case HEX_FOREST:
  896.          case HEX_RUGGED:
  897.             time++;
  898.          case HEX_BRUSH:
  899.          case HEX_PLAINS:
  900.          case HEX_DESERT:
  901.             invalid = FALSE;
  902.             break;
  903.          default:
  904.             invalid = TRUE;
  905.       }
  906.       if (invalid) {
  907.          if (PLAYER.show&SHOW_REQ) {
  908.             playSound(DONK_SOUND,PLAYER.snd_vol);
  909.             alert(map_window,"Information...","You can't build an airbase\non that kind of terrain.","Okay");
  910.          FI
  911.       FI
  912.  
  913.       if (city_hereP(unit->col,unit->row)) {
  914.          if (PLAYER.show&SHOW_REQ) {
  915.             playSound(DONK_SOUND,PLAYER.snd_vol);
  916.             alert(map_window,"Information...","You can't build an airbase on a city.","Okay");
  917.          FI
  918.          invalid = TRUE;
  919.       FI
  920.  
  921.       // airbases can't be stacked
  922.       for (scan=(struct Unit *)unit_list.mlh_Head;scan->unode.mln_Succ;scan=(struct Unit *)scan->unode.mln_Succ)
  923.          if (scan->col==unit->col && scan->row==unit->row)
  924.             if (scan->type==AIRBASE) {
  925.                if (PLAYER.show&SHOW_REQ) {
  926.                   playSound(DONK_SOUND,PLAYER.snd_vol);
  927.                   alert(map_window,"Information...","That sector already has an airbase.","Okay");
  928.                FI
  929.                invalid = TRUE;
  930.             FI
  931.  
  932.       /*
  933.          It is theoretically possible, under exotic conditions, for construction of
  934.          an airstrip to begin, but for the locaton to become invalid before it is
  935.          completed.  In this case, the orders must be cleared and we abort.
  936.       */
  937.       if (invalid==TRUE && unit->orders!=NULL) {
  938.          FreeVec(unit->orders);
  939.          unit->orders = NULL;
  940.          return;
  941.       FI
  942.    }
  943.  
  944.    /*
  945.       If the unit is already working on an airbase, we'll process the order
  946.       for this turn.  Otherwise we will issue the order to it.
  947.    */
  948.    if (unit->orders) {
  949.       if (unit->orders->etc<=turn) {
  950.          struct Unit *scan;
  951.          long count = 0;
  952.  
  953.          FreeVec(unit->orders);
  954.          unit->orders = NULL;
  955.          unit->type = AIRBASE;
  956.          unit->move = 0;
  957.          unit->cargo = 0;
  958.          unit->weight = 0;
  959.  
  960.          // If there are aircraft already in the sector, I should land them.
  961.          for (scan=(struct Unit *)unit_list.mlh_Head;scan->unode.mln_Succ;scan=(struct Unit *)scan->unode.mln_Succ) {
  962.             if (scan->col==unit->col && scan->row==unit->row)
  963.                if (wishbook[scan->type].range>0) {      // identify aircraft
  964.                   board_ship(scan,scan->col,scan->row);
  965.                   if (scan->ship)
  966.                      count++;    // count how many boarded successfully
  967.                FI
  968.          OD
  969.          if (count>0 && PLAYER.show&SHOW_REQ) {
  970.             sprintf(foo,"%ld aircraft landed on new airbase.",count);
  971.             playSound(DONK_SOUND,PLAYER.snd_vol);
  972.             alert(map_window,"Information...",foo,"Okay");
  973.          FI
  974.       FI
  975.    } else {
  976.       if (unit->type==RIFLE)
  977.          give_orders(unit,ORDER_AIRBASE,0,0,turn+time);
  978.       else if (PLAYER.show&SHOW_REQ) {
  979.          playSound(DONK_SOUND,PLAYER.snd_vol);
  980.          alert(map_window,"Information...","Only infantry can construct an airbase.","Okay");
  981.       FI
  982.    FI
  983. }
  984.  
  985.  
  986.  
  987. /*
  988.    The mode_manager() function handles the switching between survey mode,
  989.    movement mode, and any other user modes that I might add later.  In fact,
  990.    it now handles the human player's entire turn, replacing the older
  991.    human_player_moves() function.
  992. */
  993.  
  994. void mode_manager()
  995. {
  996.    int unit_count = 0, new_units;
  997.    struct Unit *current_unit = NULL;
  998.    struct Unit *unit = (struct Unit *)unit_list.mlh_Head;
  999.  
  1000.    /*
  1001.       add the movement points on all the player's units, so they are ready
  1002.       to move for this turn -- also "explore" around each unit, thus
  1003.       updating the map to account for enemy movements
  1004.  
  1005.       NOTE: if the game has just been restored from disk, we skip right into the
  1006.       player's turn -- no movement refresh or city production
  1007.    */
  1008.    if (control_flag!=GAME_RESTORED) {
  1009.       for (; unit->unode.mln_Succ; unit = (struct Unit *)unit->unode.mln_Succ)
  1010.          if (unit->owner==player) {
  1011.             unit->move = unit_speed(unit);
  1012.             unit->attacks = 0;
  1013.             explore_at_hex(player,unit->col,unit->row,INVISIBLE,FALSE);
  1014.             if (unit->orders) {
  1015.                unit->orders->processed = FALSE;
  1016.                if (unit->orders->type==ORDER_SENTRY)
  1017.                   continue;  // don't count sentried units
  1018.             FI
  1019.             unit_count++;
  1020.          FI
  1021.  
  1022.       // do the movement/survey mode thing
  1023.  
  1024.       // update his production for this turn
  1025.       new_units = do_cities_production();
  1026.  
  1027.       if (unit_count+new_units<1) {
  1028.          // even if the player has no active units, he may still see the
  1029.          // automatic combat report
  1030.          if (PLAYER.autorpt)
  1031.             show_combat_report(TRUE);
  1032.          return;     // there was nothing for him to do
  1033.       FI
  1034.    FI
  1035.  
  1036.    if (PLAYER.autorpt)
  1037.       show_combat_report(TRUE);
  1038.  
  1039.    control_flag = NULL;
  1040.    FOREVER {
  1041.       int ready;
  1042.       switch (control_flag) {
  1043.          case GO_SURVEY:
  1044.             survey_mode(¤t_unit);
  1045.             break;
  1046.          case GO_PRODUCTION:
  1047.             production_mode();
  1048.             if (control_flag==GO_MOVEMENT && Bool(current_unit))
  1049.                set_display_offsets(current_unit->col,current_unit->row);
  1050.             GP_draw_map();    // redraw the map for use of movement or survey mode!
  1051.             break;
  1052.          case EXIT_GAME:
  1053.          case GAME_OVER:
  1054.          case END_TURN:
  1055.             goto eot_cleanup;
  1056.          default:
  1057.             ready = unit_readiness(current_unit);
  1058.             if (ready&(UNIT_UNREADY|UNIT_PROCESSED))
  1059.                current_unit = choose_default_unit(NULL);
  1060.             ready = unit_readiness(current_unit);
  1061.             if (ready&UNIT_ENGAGED)
  1062.                order_manager(current_unit);
  1063.             else
  1064.                movement_mode(¤t_unit);
  1065.             if (control_flag==UNIT_LOST)
  1066.                current_unit=NULL;
  1067.       }
  1068.    OD
  1069.  
  1070.  eot_cleanup:     // do some housekeeping at end of turn
  1071.    // here I want to check for any ships that need repair
  1072.    {
  1073.       struct Unit *unit=(struct Unit *)unit_list.mlh_Head;
  1074.       for (; unit->unode.mln_Succ; unit=(struct Unit *)unit->unode.mln_Succ)
  1075.          if (unit->damage>0 && unit->move>50)
  1076.             if (unit->owner==player && city_hereP(unit->col,unit->row))
  1077.                unit->damage--;
  1078.    }
  1079.  
  1080.  
  1081.    SetWindowTitles(map_window,"Game in progress...",(UBYTE *)~0);
  1082. }
  1083.  
  1084. //UBYTE *get_password()
  1085. //{
  1086. // struct Window *PwordWindow;
  1087. // short player = Opt.CurrentPlayer;
  1088. // struct IntuiMessage *message; // the message the IDCMP sends us
  1089. // short quit_flag = FALSE;
  1090. //
  1091. // strcpy(PWordIText53.IText,Roster[player].Name);
  1092. //
  1093. // PWordNewWindowStructure7.Screen = MapScreen;
  1094. // PwordWindow = OpenWindow(&PWordNewWindowStructure7);
  1095. // if (PwordWindow==NULL) {
  1096. //    Print("Unable to open password window!\n");
  1097. //    clean_exit();
  1098. // FI
  1099. // SetPointer(MapWindow,BUSY_POINTER);
  1100. //
  1101. // PrintIText(PwordWindow->RPort,&PWordIText53,0,0);
  1102. //
  1103. // do {
  1104. //    WaitPort(PwordWindow->UserPort);
  1105. //       while( (message = (struct IntuiMessage *)\
  1106. //          GetMsg(PwordWindow->UserPort) ) != NULL)
  1107. //       {
  1108. //          code = message->Code;  // MENUNUM
  1109. //          object = message->IAddress;  // Gadget
  1110. //          class = message->Class;
  1111. //          ReplyMsg(message);
  1112. //          if ( class == CLOSEWINDOW )
  1113. //             (quit_flag = TRUE);
  1114. //       OD
  1115. // } while (quit_flag == FALSE);
  1116. // CloseWindow(PwordWindow);
  1117. // PwordWindow = NULL;
  1118. // ClearPointer(MapWindow);
  1119. //
  1120. // return PWordPWordPWordSIBuff;
  1121. //}
  1122.  
  1123. /*
  1124.    This gets a password from the user and combines it with his name
  1125.    to create a unique passlock string.  This will be used for filemail
  1126.    security checks.  NOTE: This is a really quick-and-dirty approach
  1127.    offering _minimal_ security for the users.  To be perfectly honest,
  1128.    I am just too lazy to sit down and implement real serious security
  1129.    for this game.  If you can't trust the other guy, why are you playing
  1130.    with him anyway?
  1131. */
  1132.  
  1133. /*void create_passlock()
  1134. {
  1135.    char *password = get_password();
  1136.    short ctr = 0, pw_index = 0, nm_index = 0;
  1137.  
  1138.    for (; ctr<=40; ctr++) {
  1139.       PASSLOCK[ctr] = password[pw_index++] ^ NAME[nm_index++];
  1140.       if (password[pw_index]=='\0')
  1141.          pw_index=0;
  1142.       if (NAME[nm_index]=='\0')
  1143.          nm_index=0;
  1144.    OD
  1145.  
  1146.    // erase the password itself
  1147.    for (ctr=0; ctr<=40; ctr++)
  1148.       password[ctr] = '\0';
  1149. }
  1150. */
  1151.  
  1152. /*
  1153.    Here is where I get the password and compare it with the user's
  1154.    passlock string, which was created earlier.
  1155. */
  1156.  
  1157. /*int verify_password()
  1158. {
  1159.    char *password = get_password(), newlock[41];
  1160.    short ctr = 0, pw_index = 0, nm_index = 0;
  1161.  
  1162.    for (; ctr<=40; ctr++) {
  1163.       newlock[ctr] = password[pw_index++] ^ NAME[nm_index++];
  1164.       if (password[pw_index]=='\0')
  1165.          pw_index=0;
  1166.       if (NAME[nm_index]=='\0')
  1167.          nm_index=0;
  1168.    OD
  1169.    if (strncmp(newlock,PASSLOCK,40L)==0)
  1170.       return TRUE;
  1171.    else
  1172.       return FALSE;
  1173. }
  1174.  
  1175. void show_report()
  1176. {
  1177.    long file;
  1178.    short ctr;
  1179.  
  1180.    if (Opt.FMailMode) {
  1181.       sprintf(report,"Movement and battle report for turn %ld.",\
  1182.       Opt.CurrentTurn);
  1183.       tell_user(report,NULL);
  1184.    } else
  1185.       tell_user("All commanders report for movement and battle phase!",NULL);
  1186.    clear_map();
  1187.    draw_mapgrid();
  1188.    DisplayBeep(MapScreen);
  1189.    SetPointer(MapWindow,WAIT_POINTER);
  1190.    wait_for_click();
  1191.    SetPointer(MapWindow,BUSY_POINTER);
  1192.  
  1193.    file = Open("T:RPT.CC",MODE_OLDFILE);
  1194.    if (file==NULL)
  1195.       return;
  1196.    for (ctr=1; ctr<=(FLength("T:RPT.CC")/sizeof(struct ReportRecord)); ctr++) {
  1197.       Read(file,&MBReport,(long)sizeof(MBReport));
  1198.       show_entry(&MBReport);
  1199.    OD
  1200.    Close(file);
  1201.  
  1202.    SetPointer(MapWindow,WAIT_POINTER);
  1203.    tell_user("Click to continue...",NULL);
  1204.    wait_for_click();
  1205.    ClearPointer(MapWindow);
  1206. }
  1207. */
  1208.  
  1209. /*
  1210.    Next function returns the speed of a unit.  I can't use a simple table
  1211.    here because the speed of some units vary, such as ships when damaged.
  1212. */
  1213.  
  1214. int unit_speed(unit)
  1215. struct Unit *unit;
  1216. {
  1217.    int speed = wishbook[unit->type].speed;  // base speed of each unit type
  1218.  
  1219.    /*
  1220.       Note that speed in Invasion Force is measured by 60ths of a hex.
  1221.       Thus, a RIFLE unit with speed 60 can generally move one hex across
  1222.       open (HEX_PLAINS) terrain.  A SUB with speed 120 will usually move
  1223.       two hexes through open ocean (HEX_OCEAN).  The final result will
  1224.       vary depending on various factors: terrain, combat, ship damage, etc.
  1225.    */
  1226.  
  1227.    // compensate for ship damage here...
  1228.    switch (unit->type) {
  1229.       case ARMOR:
  1230.          if (unit->damage>=1)
  1231.             speed -= 60;
  1232.          break;
  1233.       case DESTROYER:
  1234.          if (unit->damage>=2)
  1235.             speed -= 120;
  1236.          break;
  1237.       case TRANSPORT:
  1238.          if (unit->damage>=2)
  1239.             speed -= 60;
  1240.          break;
  1241.       case SUB:
  1242.          if (unit->damage>=1)
  1243.             speed -= 60;
  1244.          break;
  1245.       case CRUISER:
  1246.       case CARRIER:
  1247.          if (unit->damage>=4)
  1248.             speed -= 60;
  1249.          break;
  1250.       case BATTLESHIP:
  1251.          if (unit->damage>=6)
  1252.             speed -= 60;
  1253.    }
  1254.    return speed;
  1255. }
  1256.  
  1257.  
  1258. /*
  1259.    The following function calls the player to the computer (presumably because
  1260.    it's his turn) and generates his map display, centered on (row,col),
  1261.    presumably because some event has happened there.  This is what I call when
  1262.    jumpstarting a player, or when his first unit is produced on a turn, or
  1263.    when he needs to move his first unit on a turn when nothing was produced.
  1264.    The function is aware of the current player's ".show" value, so you don't
  1265.    need to check it before calling.
  1266. */
  1267.  
  1268. void create_player_display(col,row)
  1269. int col,row;
  1270. {
  1271.    // Do nothing if there is already a map display.
  1272.    if (display==TRUE) return;
  1273.  
  1274.    // first, call the player over here
  1275.    if (PLAYER.show&SHOW_REQ) {
  1276.       sprintf(foo,"Turn %ld.  Player %ld up.\n%s report for duty!",turn,player,PLAYER.name);
  1277.       (void)rtEZRequestTags(foo,"I'm Here",NULL,NULL,
  1278.          RTEZ_ReqTitle,NULL,
  1279.          RTEZ_Flags,EZREQF_CENTERTEXT,
  1280.          RT_Window,map_window,
  1281.          RT_ReqPos,REQPOS_CENTERWIN,
  1282.          RT_LockWindow, TRUE,
  1283.          TAG_END );
  1284.    FI
  1285.  
  1286.    // now create the display
  1287.    if (PLAYER.show&SHOW_GRP) {
  1288.       set_display_offsets(col,row);
  1289.       GP_draw_map();
  1290.       // set the display flag, so the program will know he's already got a
  1291.       // map, and won't needlessly call the player and draw the map again
  1292.       display = TRUE;
  1293.    FI
  1294. }
  1295.  
  1296.  
  1297. void weed_combat_report()
  1298. {  // weed old messages out of the combat report file
  1299.    BPTR oldfile, newfile;
  1300.    ULONG length;
  1301.    int ctr, kill;
  1302.  
  1303.    strcpy(foo,prefix);
  1304.    strcat(foo,id_filetag);
  1305.    strcat(foo,".CR");   // CR = Combat Report
  1306.    if ((length=FLength(foo))<1)
  1307.       return;  // no combat report to work with
  1308.    oldfile = Open(foo,MODE_OLDFILE);
  1309.    strcpy(bar,foo);
  1310.    strcat(bar,"2");
  1311.    newfile = Open(bar,MODE_NEWFILE);
  1312.    for (ctr=0; ctr<(length/sizeof(battle)); ctr++) {
  1313.       Read(oldfile,&battle,sizeof(battle));
  1314.       kill = FALSE;
  1315.       if (battle.turn<turn-1)
  1316.          kill = TRUE;
  1317.       if (battle.turn==turn-1 && battle.att_owner<player)
  1318.          kill = TRUE;
  1319.       if (!kill)
  1320.          Write(newfile,&battle,sizeof(battle));
  1321.    OD
  1322.    Close(oldfile);
  1323.    Close(newfile);
  1324.    DeleteFile(foo);  // delete old combat report file
  1325.    Rename(bar,foo);  // rename new file to replace old one
  1326. }
  1327.  
  1328.  
  1329. BOOL save_game(filename)
  1330. char *filename;
  1331. {
  1332.    struct rtHandlerInfo *handle;
  1333.    BPTR file;
  1334.    int ctr;
  1335.    char err[80];
  1336.  
  1337.    sprintf(foo,"Save game to file:\n   `%s'",filename);
  1338.    handle = post_it(foo);
  1339.    SetPointer(map_window,BUSY_POINTER);
  1340.  
  1341.    file = Open(filename,MODE_NEWFILE);
  1342.    if (file==NULL) {
  1343.       strcpy(err,"Unable to open output file!");
  1344.       goto save_error;
  1345.    FI
  1346.  
  1347.    // the basic header information
  1348.    Write(file,"EMP2GAME",8L);    // magic identifies my saved games
  1349.    Write(file,&revision,2L);     // identify revision of Invasion Force
  1350.    Write(file,id_filetag,4L);    // the ID tag used for temporary filenames
  1351.  
  1352.    // the game status
  1353.    Write(file,&opt,(long)sizeof(opt));        // game options
  1354.    Write(file,&turn,(long)sizeof(turn));      // current turn
  1355.    Write(file,&player,(long)sizeof(player));  // current player
  1356.    Write(file,wishbook,(long)sizeof(wishbook));   // the unit definitions
  1357.  
  1358.    // the game map
  1359.    Write(file,&wrap,(long)sizeof(wrap));
  1360.    Write(file,&width,(long)sizeof(width));
  1361.    Write(file,&height,(long)sizeof(height));
  1362.    Write(file,t_grid,GRID_SIZE*2);
  1363.  
  1364.    // the city list
  1365.    {
  1366.       struct City *metro=(struct City *)city_list.mlh_Head;
  1367.       int num_cities=count_nodes(&city_list);
  1368.  
  1369.       Write(file,&num_cities,(long)sizeof(num_cities));
  1370.       for (; metro->cnode.mln_Succ; metro=(struct City *)metro->cnode.mln_Succ)
  1371.          Write(file,&metro->col,(long)(sizeof(*metro)-sizeof(metro->cnode)));
  1372.    }
  1373.  
  1374.    // the unit list
  1375.    /*
  1376.       When writing a unit, unlike cities or mapicons, I must also save the
  1377.       address.  This will allow cargo units to be re-linked with their ships
  1378.       when loading them from disk.  I also must save the subordinate data such
  1379.       as orders or a name, if present.
  1380.    */
  1381.    {
  1382.       struct Unit *unit=(struct Unit *)unit_list.mlh_Head;
  1383.       int num_units=count_nodes(&unit_list);
  1384.  
  1385.       Write(file,&num_units,(long)sizeof(num_units));
  1386.       for (; unit->unode.mln_Succ; unit=(struct Unit *)unit->unode.mln_Succ) {
  1387.          if (unit->cargo>0)
  1388.             unit->ship = unit;
  1389.          Write(file,&unit->col,(long)(sizeof(*unit)-sizeof(unit->unode)));
  1390.          if (unit->cargo>0)
  1391.             unit->ship = NULL;
  1392.          if (unit->orders)
  1393.             Write(file,unit->orders,sizeof(*unit->orders));
  1394.          if (unit->name) {
  1395.             short size=strlen(unit->name)+1;
  1396.             Write(file,&size,2L);               // length of unit name
  1397.             Write(file,unit->name,(long)size);  // the name itself
  1398.          FI
  1399.       OD
  1400.    }
  1401.  
  1402.    // the roster of players
  1403.    Write(file,roster,(long)sizeof(roster));
  1404.  
  1405.    // save the map data for each player
  1406.    for (ctr=1; ctr<=8; ctr++)
  1407.       if (roster[ctr].type!=NOPLAYER)
  1408.          Write(file,roster[ctr].map,GRID_SIZE*2);
  1409.  
  1410.    // save the map icon list for each player
  1411.    for (ctr=1; ctr<=8; ctr++)
  1412.       if (roster[ctr].type!=NOPLAYER) {
  1413.          struct MapIcon *icon=(struct MapIcon *)roster[ctr].icons.mlh_Head;
  1414.          int num_icons=count_nodes(&roster[ctr].icons);
  1415.  
  1416.          Write(file,&num_icons,(long)sizeof(num_icons));
  1417.          for (; icon->inode.mln_Succ; icon=(struct MapIcon *)icon->inode.mln_Succ)
  1418.             Write(file,&icon->col,(long)(sizeof(*icon)-sizeof(icon->inode)));
  1419.       FI
  1420.  
  1421.    // save the combat report
  1422.    {
  1423.       BPTR infile;
  1424.       ULONG length;
  1425.  
  1426.       strcpy(foo,prefix);
  1427.       strcat(foo,id_filetag);
  1428.       strcat(foo,".CR");
  1429.       length=FLength(foo);
  1430.       Write(file,&length,(long)sizeof(length));
  1431.       if (length>=sizeof(battle)) {
  1432.          infile = Open(foo,MODE_OLDFILE);
  1433.          if (infile) {
  1434.             for (ctr=0; ctr<(length/sizeof(battle)); ctr++) {
  1435.                Read(infile,&battle,(long)sizeof(battle));
  1436.                Write(file,&battle,(long)sizeof(battle));
  1437.             OD
  1438.             Close(infile);
  1439.          FI
  1440.       FI
  1441.    }
  1442.  
  1443.    // save the specialized AI players' data
  1444.    if( SaveAIPlayers( file, err ) ) goto save_error;
  1445.  
  1446.    Close(file);
  1447.  
  1448.    // set the filenote
  1449.    SetComment(filename,"Invasion Force -- saved game.");
  1450.  
  1451.    ClearPointer(map_window);
  1452.    alert(map_window,NULL,"Game saved!","Okay");
  1453.    unpost_it(handle);
  1454.    return TRUE;
  1455.  
  1456. save_error:
  1457.    // to handle any errors that occur when attempting to save the game
  1458.    if (file)
  1459.       Close(file);
  1460.    DisplayBeep(map_screen);
  1461.    sprintf(foo,"Error saving game file `%s'.\n%s",filename,err);
  1462.    alert(map_window,NULL,foo,"Drat!");
  1463.    unpost_it(handle);
  1464.    ClearPointer(map_window);
  1465.    return FALSE;
  1466. }
  1467.  
  1468.  
  1469. BOOL load_game(filename)
  1470. char *filename;
  1471. {
  1472.    struct rtHandlerInfo *handle;
  1473.    BPTR file;
  1474.    int ctr;
  1475.    short revcheck;
  1476.    char err[80];
  1477.  
  1478.    strcpy(err,"The file seems to be incomplete.");    // default error message
  1479.  
  1480.    sprintf(foo,"Load game from file:\n   `%s'",filename);
  1481.    handle = post_it(foo);
  1482.    SetPointer(map_window,BUSY_POINTER);
  1483.  
  1484.    if ((file=Open(filename,MODE_OLDFILE))==NULL) {
  1485.       strcpy(err,"The file could not be opened.");
  1486.       goto load_error;
  1487.    FI
  1488.  
  1489.    // the basic header information
  1490.    (void)Read(file,foo,8L);
  1491.    if (strncmp(foo,"EMP2GAME",8)) {
  1492.       strcpy(err,"That does not seem to be an Invasion Force game.");
  1493.       goto load_error;  // not a saved game
  1494.    FI
  1495.    (void)Read(file,&revcheck,2L);
  1496.    if (revcheck<6 || revcheck>revision) {
  1497.  
  1498.       sprintf(err,"That is not an Invasion Force 0.06--0.%d map file!", revision);
  1499.       goto load_error;
  1500.    FI
  1501.    if (revcheck<revision) {
  1502.       (void)rtEZRequestTags(\
  1503.          "WARNING!\nThat appears to be an older revision file.\nYou should re-save it for future compatibility.",
  1504.          "Duly Noted",NULL,NULL,
  1505.          RT_Window,        map_window,
  1506.          RT_ReqPos,        REQPOS_CENTERSCR,
  1507.          RT_LockWindow,    TRUE,
  1508.          RTEZ_Flags,       EZREQF_CENTERTEXT,
  1509.          TAG_DONE );
  1510.    FI
  1511.  
  1512.    (void)Read(file,id_filetag,4L);
  1513.  
  1514.    // the game status
  1515.    (void)Read(file,&opt,sizeof(opt));           // option settings
  1516.    (void)Read(file,&turn,sizeof(turn));         // current turn
  1517.    (void)Read(file,&player,sizeof(player));     // current player
  1518.    if (revcheck>6)
  1519.       if (revcheck>16)
  1520.          (void)Read(file,wishbook,sizeof(wishbook));
  1521.       else
  1522.          (void)Read(file,wishbook,sizeof(struct UnitTemplate)*11L);
  1523.  
  1524.    // the game map
  1525.    (void)Read(file,&wrap,sizeof(wrap));
  1526.    (void)Read(file,&width,sizeof(width));
  1527.    if (Read(file,&height,sizeof(height))<sizeof(height))
  1528.       goto load_error;
  1529.  
  1530.    if (alloc_map(&t_grid)==FALSE) {
  1531.       strcpy(err,"Unable to allocate RAM for terrain.");
  1532.       goto load_error;
  1533.    FI
  1534.    if (revcheck<13) {
  1535.       if (Read(file,t_grid,GRID_SIZE)<GRID_SIZE)
  1536.          goto load_error;
  1537.    } else {
  1538.       if (Read(file,t_grid,GRID_SIZE*2)<GRID_SIZE*2)
  1539.          goto load_error;
  1540.    }
  1541.  
  1542.    // convert the old ARCTIC terrain to plains
  1543.    if (revcheck<13) {
  1544.       int cx, cy, terra;
  1545.       for (cx=0; cx<width; cx++)
  1546.          for (cy=0; cy<height; cy++) {
  1547.             terra = get(t_grid,cx,cy);
  1548.             if (terra==HEX_ARCTIC) {
  1549.                terra = HEX_PLAINS;
  1550.                put(t_grid,cx,cy,terra);
  1551.             FI
  1552.          OD
  1553.    FI
  1554.  
  1555.    // the city list
  1556.    {
  1557.       int num_cities;
  1558.  
  1559.       (void)Read(file,&num_cities,sizeof(num_cities));
  1560.       if (num_cities>0) {
  1561.          struct City *metro;
  1562.  
  1563.          for (ctr=1; ctr<=num_cities; ctr++) {
  1564.             metro = AllocVec(sizeof(*metro),MEMF_CLEAR);
  1565.             if (metro==NULL) {
  1566.                strcpy(err,"Unable to allocate RAM for cities.");
  1567.                goto load_error;
  1568.             FI
  1569.             if (revcheck<9) {
  1570.                struct OldCity oldmetro;
  1571.                int ctr2;
  1572.  
  1573.                (void)Read(file,&metro->col,sizeof(oldmetro)-sizeof(oldmetro.cnode));
  1574.                for (ctr2=0;ctr2<9;ctr2++)
  1575.                   metro->recon[ctr2] = CITY;   // no recon info
  1576.             } else
  1577.                (void)Read(file,&metro->col,sizeof(*metro)-sizeof(metro->cnode));
  1578.             if (revcheck<10)
  1579.                metro->specialty = CITY;
  1580.             AddTail((struct List *)&city_list,(struct Node *)metro);
  1581.          OD
  1582.       FI
  1583.    }
  1584.  
  1585.    // the unit list
  1586.    /*
  1587.       Loading in the units is complex because of the sub-items "orders"
  1588.       and "name" and even more importantly because of the linking between
  1589.       ships and the units they carry.  I must build a table of ships with
  1590.       their orginal addresses so that the units on board can find them and
  1591.       relink to the new addresses.  Although this makes loading complex, it
  1592.       is worthwhile for the simplicity of handing ship cargo in the game.
  1593.  
  1594.       The original address of the ship (in the previous session) has been
  1595.       stored in its unit->ship field.
  1596.    */
  1597.    {
  1598.       int num_units;
  1599.  
  1600.       if (Read(file,&num_units,sizeof(num_units))<sizeof(num_units))
  1601.          goto load_error;
  1602.  
  1603.       if (num_units>0) {
  1604.          int num_ships=0;
  1605.          APTR shiptab[100][2];    // table for re-linking cargo ships
  1606.          struct Unit *unit;
  1607.  
  1608.          for (ctr=1; ctr<=num_units; ctr++) {
  1609.             if ((unit=AllocVec(sizeof(*unit),MEMF_CLEAR))==NULL) {
  1610.                strcpy(err,"Unable to allocate RAM for units.");
  1611.                goto load_error;
  1612.             FI
  1613.             if (revcheck<14) {
  1614.                struct OldUnit compat;
  1615.                long ldsz = sizeof(compat)-sizeof(compat.unode);
  1616.  
  1617.                if (Read(file,&compat.col,ldsz)<ldsz) {
  1618.                   unit->col = compat.col;
  1619.                   unit->row = compat.row;
  1620.                   unit->owner = compat.owner;
  1621.                   unit->type = compat.type;
  1622.                   unit->damage = compat.damage;
  1623.                   unit->attacks = compat.attacks;
  1624.                   unit->cargo = compat.cargo;
  1625.                   unit->weight = compat.cargo;
  1626.                   unit->move = compat.move;
  1627.                   unit->fuel = compat.fuel;
  1628.                   unit->ship = compat.ship;
  1629.                   unit->orders = compat.orders;
  1630.                   unit->name = compat.name;
  1631.                } else
  1632.                   goto load_error;
  1633.             } else
  1634.                if (Read(file,&unit->col,sizeof(*unit)-sizeof(unit->unode))<sizeof(*unit)-sizeof(unit->unode)) {
  1635.                   FreeVec(unit);
  1636.                   goto load_error;
  1637.                FI
  1638.  
  1639.             // record table data for relinking ships and cargo
  1640.             if (unit->cargo>0) {
  1641.                shiptab[num_ships][0] = unit->ship;
  1642.                shiptab[num_ships++][1] = unit;
  1643.                unit->ship = NULL;
  1644.             FI
  1645.  
  1646.             AddTail((struct List *)&unit_list,(struct Node *)unit);
  1647.             if (unit->orders) {   // load in the orders
  1648.                unit->orders = AllocVec((long)sizeof(*unit->orders),MEMF_CLEAR);
  1649.                if (unit->orders==NULL) {
  1650.                   strcpy(err,"Unable to allocate RAM for unit orders.");
  1651.                   goto load_error;
  1652.                FI
  1653.                if (revcheck<8) {
  1654.                   struct OldOrder compat;
  1655.  
  1656.                   (void)Read(file,&compat,sizeof(compat));
  1657.                   unit->orders->type = compat.type;
  1658.                   unit->orders->orgx = compat.orgx;
  1659.                   unit->orders->orgy = compat.orgy;
  1660.                   unit->orders->destx = compat.destx;
  1661.                   unit->orders->desty = compat.desty;
  1662.                   unit->orders->etc = compat.etc;
  1663.                   unit->orders->dest_unit = compat.dest_unit;
  1664.                   unit->orders->processed = TRUE;
  1665.                } else
  1666.                   (void)Read(file,unit->orders,sizeof(*unit->orders));
  1667.             FI
  1668.             if (unit->name) {
  1669.                short size;
  1670.                if (Read(file,&size,2L)<2L)
  1671.                   goto load_error;
  1672.  
  1673.                if (Read(file,foo,(long)size)<(long)size)
  1674.                   goto load_error;
  1675.  
  1676.                unit->name = NULL;   // so name_unit() doesn't destruct
  1677.                name_unit(unit,foo);
  1678.             FI
  1679.          OD
  1680.  
  1681.          // run through the whole list to relink cargo units to their ships
  1682.          unit=(struct Unit *)unit_list.mlh_Head;
  1683.          for (; unit->unode.mln_Succ; unit=(struct Unit *)unit->unode.mln_Succ)
  1684.             if (unit->ship) {
  1685.                APTR shipid=unit->ship;
  1686.  
  1687.                unit->ship = NULL;
  1688.                for (ctr=0; ctr<num_ships; ctr++)
  1689.                   if (shiptab[ctr][0]==shipid) {
  1690.                      unit->ship = shiptab[ctr][1];
  1691.                      break;
  1692.                   FI
  1693.             FI
  1694.       FI
  1695.    }
  1696.  
  1697.    // the roster of players
  1698.    if (revcheck>=8)
  1699.       if (Read(file,roster,sizeof(roster))<sizeof(roster))
  1700.          goto load_error;
  1701.    // conversion of percentage-based attack and defense modifiers
  1702.    if (revcheck<13)
  1703.       for (ctr=0; ctr<9; ctr++) {
  1704.          roster[ctr].att = roster[ctr].att*16/100-8;
  1705.          roster[ctr].def = roster[ctr].def*16/100-8;
  1706.       }
  1707.  
  1708.    if (revcheck<8) {    // conversion for old files
  1709.       int ctr, ctr2;
  1710.       struct OldPLayer compat[9];
  1711.       if (Read(file,compat,sizeof(compat))<sizeof(compat))
  1712.          goto load_error;
  1713.       for (ctr=0; ctr<9; ctr++) {
  1714.          strcpy(roster[ctr].name,compat[ctr].name);
  1715.          roster[ctr].type   = compat[ctr].type;
  1716.          roster[ctr].status = compat[ctr].status;
  1717.          roster[ctr].color  = compat[ctr].color;
  1718.          roster[ctr].prod   = compat[ctr].prod;
  1719.          roster[ctr].att    = compat[ctr].att;
  1720.          roster[ctr].def    = compat[ctr].def;
  1721.          roster[ctr].aggr   = compat[ctr].aggr;
  1722.          roster[ctr].msg_delay    = compat[ctr].msg_delay;
  1723.          roster[ctr].battle_delay = compat[ctr].battle_delay;
  1724.          roster[ctr].soundfx      = compat[ctr].soundfx;
  1725.          roster[ctr].autorpt      = compat[ctr].autorpt;
  1726.          roster[ctr].show_production = compat[ctr].show_production;
  1727.          for (ctr2=0; ctr2<11; ctr2++) {
  1728.             roster[ctr].eud[ctr2] = compat[ctr].eud[ctr2];
  1729.             roster[ctr].ulc[ctr2] = compat[ctr].ulc[ctr2];
  1730.          OD
  1731.          roster[ctr].show = SHOW_ALL;
  1732.       OD
  1733.    FI
  1734.  
  1735.    // read the map data for each player
  1736.    for (ctr=1; ctr<=8; ctr++) {
  1737.       if (roster[ctr].type!=NOPLAYER) {
  1738.          roster[ctr].map = NULL;    // so alloc_map() doesn't try to destruct
  1739.          if (!alloc_map(&roster[ctr].map)) {
  1740.             strcpy(err,"Unable to allocate RAM for player maps!");
  1741.             goto load_error;
  1742.          FI
  1743.          if (revcheck<13)
  1744.             (void)Read(file,roster[ctr].map,GRID_SIZE);
  1745.          else
  1746.             (void)Read(file,roster[ctr].map,GRID_SIZE*2);
  1747.  
  1748.          // convert the old ARCTIC terrain to plains
  1749.          if (revcheck<13) {
  1750.             int cx, cy, terra;
  1751.             for (cx=0; cx<width; cx++)
  1752.                for (cy=0; cy<height; cy++) {
  1753.                   terra = get(roster[ctr].map,cx,cy);
  1754.                   if (terra==HEX_ARCTIC) {
  1755.                      terra = HEX_PLAINS;
  1756.                      put(roster[ctr].map,cx,cy,terra);
  1757.                   FI
  1758.                OD
  1759.          FI
  1760.       FI
  1761.    }
  1762.  
  1763.    // read the map icon list for each player
  1764.    for (ctr=1; ctr<=8; ctr++)
  1765.       if (roster[ctr].type!=NOPLAYER) {
  1766.          int num_icons, n;
  1767.          struct MapIcon *icon;
  1768.  
  1769.          if (Read(file,&num_icons,sizeof(num_icons))<sizeof(num_icons))
  1770.             goto load_error;
  1771.          NewList((struct List *)&roster[ctr].icons);  // new icon list
  1772.          for (n=0; n<num_icons; n++) {
  1773.             icon = AllocVec(sizeof(*icon),MEMF_CLEAR);
  1774.             if (icon==NULL) {
  1775.                strcpy(err,"Unable to allocate RAM for player map icons!");
  1776.                goto load_error;
  1777.             FI
  1778.             if (revcheck>=10)
  1779.                if (Read(file,&icon->col,sizeof(*icon)-sizeof(icon->inode))<(sizeof(*icon)-sizeof(icon->inode)))
  1780.                   goto load_error;
  1781.             if (revcheck<10) {
  1782.                struct OldMapIcon omi;
  1783.  
  1784.                if (Read(file,&omi.col,sizeof(omi)-sizeof(omi.inode))<(sizeof(omi)-sizeof(omi.inode)))
  1785.                   goto load_error;
  1786.                icon->col = omi.col;
  1787.                icon->row = omi.row;
  1788.                icon->type = omi.type;
  1789.                icon->owner = omi.owner;
  1790.                icon->token = omi.token;
  1791.                icon->turn = turn;
  1792.                icon->stacked = omi.stacked;
  1793.             FI
  1794.             // value of MILITIA and CITY changed in release 0.13
  1795.             if (revcheck<13)
  1796.                if (icon->type==11 || icon->type==12)
  1797.                   icon->type += 9;
  1798.  
  1799.             AddTail((struct List *)&roster[ctr].icons,(struct Node *)icon);
  1800.          OD
  1801.       FI
  1802.  
  1803.    // restore the combat report
  1804.    {
  1805.       BPTR outfile;
  1806.       ULONG length;
  1807.  
  1808.       strcpy(foo,prefix);
  1809.       strcat(foo,id_filetag);
  1810.       strcat(foo,".CR");
  1811.       if (Read(file,&length,sizeof(length))<sizeof(length))
  1812.          goto load_error;
  1813.       outfile = Open(foo,MODE_NEWFILE);
  1814.       if (outfile==NULL) {
  1815.          strcpy(err,"Unable to create new combat report file!");
  1816.          goto load_error;
  1817.       FI
  1818.       if (length>=sizeof(battle))
  1819.          for (ctr=0; ctr<(length/sizeof(battle)); ctr++) {
  1820.             if (Read(file,&battle,(long)sizeof(battle))<sizeof(battle)) {
  1821.                Close(outfile);
  1822.                DeleteFile(foo);
  1823.                goto load_error;
  1824.             FI
  1825.             Write(outfile,&battle,(long)sizeof(battle));
  1826.          OD
  1827.       Close(outfile);
  1828.    }
  1829.  
  1830.    // And now, load up the AI players' data from the save file
  1831.    if( revcheck > 10 ) {
  1832.        // Older revisions than 11 don't have AI players data in them
  1833.        if( LoadAIPlayers( file, err ) )  goto load_error;
  1834.    }
  1835.  
  1836.    Close(file);
  1837.    ClearPointer(map_window);
  1838.    alert(map_window,NULL,"Game loaded!","Okay");
  1839.    unpost_it(handle);
  1840.    return TRUE;
  1841.  
  1842. load_error:
  1843.    /*
  1844.       This part of the function handles anything that might go wrong during
  1845.       the load process.  It allows me to abort and clean up any mess that
  1846.       might have been created.  An error text should be found in the string
  1847.       variable "err" to inform the user.
  1848.    */
  1849.  
  1850.    // alert the user that something went wrong
  1851.    DisplayBeep(map_screen);
  1852.    sprintf(foo,"Error loading game file `%s'.\n%s",filename,err);
  1853.    alert(map_window,NULL,foo,"Drat!");
  1854.    unpost_it(handle);
  1855.  
  1856.    // clean up any messes that may have been left from the load process
  1857.    if (file)
  1858.       Close(file);
  1859.    cleanup_game();
  1860.    ClearPointer(map_window);
  1861.  
  1862.    return FALSE;
  1863. }
  1864.  
  1865.  
  1866. /*
  1867.    the cryptically named function build_pan() actually accepts a path
  1868.    and a filename and builds them into a supposedly valid "path and name"
  1869.    (or PAN) in a single string
  1870. */
  1871.  
  1872. void build_pan(string,path,file)
  1873. char *string, *path, *file;
  1874. {
  1875.    int dirlen=strlen(path);
  1876.  
  1877.    strcpy(string,path);
  1878.    if (dirlen)
  1879.       if (string[dirlen-1]!='/' && string[dirlen-1]!=':')
  1880.          strcat(string,"/");
  1881.    strcat(string,file);
  1882. }
  1883.  
  1884.  
  1885. BOOL rt_loadsave_game(save)
  1886. int save;
  1887. {  // load or save using the ReqTools file requester
  1888.    struct rtFileRequester *req = NULL;
  1889.    char pan[216];  // pan = Path And Name
  1890.    BOOL chosen;
  1891.  
  1892.    req = rtAllocRequestA(RT_FILEREQ,NULL);
  1893.    if (req==NULL) {
  1894.       alert(map_window,NULL,"Failed to allocate ReqTools file requester.","Drat!");
  1895.       return FALSE;
  1896.    FI
  1897.    rtChangeReqAttr(req,
  1898.       RTFI_Dir,game_filepath,
  1899.       RTFI_MatchPat,"#?.IF",
  1900.       TAG_END);
  1901.    chosen = (BOOL)rtFileRequest(req,game_filename,(save ? "Save Map" : "Load Map"),
  1902.       RTFI_Flags, (save ? FREQF_SAVE : NULL) | FREQF_PATGAD,
  1903.       RT_DEFAULT, TAG_END);
  1904.    if (chosen) {
  1905.       strcpy(game_filepath,req->Dir);
  1906.       build_pan(pan,game_filepath,game_filename);
  1907.    } else {
  1908.       rtFreeRequest(req);
  1909.       return FALSE;
  1910.    FI
  1911.    rtFreeRequest(req);
  1912.    if (save)
  1913.       return save_game(pan);
  1914.    else
  1915.       return load_game(pan);
  1916. }
  1917.  
  1918.  
  1919. void execute_game_turns()
  1920. {
  1921.    char outbuf[40];
  1922.    int  i;
  1923.  
  1924.    if (control_flag!=GAME_RESTORED) {
  1925.       /*
  1926.          We want to give the players a city to work from
  1927.          before starting each player - that way
  1928.          player #1 is not surprised by player #8
  1929.          showing up next to him (and not seeing
  1930.          him when selecting initial city production)
  1931.       */
  1932.       for (; player<=8; player++) {
  1933.          if( PLAYER.status == ACTIVE && PLAYER.type != NOPLAYER) {
  1934.             if (NONHUMAN(PLAYER.type))
  1935.                PLAYER.soundfx = SOUND_NONE;
  1936.             if (PLAYER.map == NULL)
  1937.                create_initial_city();
  1938.          }
  1939.       }
  1940.       player = 1;
  1941.    }
  1942.  
  1943.    FOREVER {
  1944.       for (; player<=8; player++) {
  1945.          clear_movebar();
  1946.          weed_combat_report();
  1947.          if (PLAYER.status == ACTIVE && PLAYER.type != NOPLAYER) {
  1948.             CLEAR_WINDOW();
  1949.             if (PLAYER.map == NULL)
  1950.                jumpstart_player();
  1951.             if (ISHUMAN(PLAYER.type))
  1952.                mode_manager();
  1953.             if (control_flag==EXIT_GAME) goto end_of_game;
  1954.             if (NONHUMAN(PLAYER.type))
  1955.                computer_player_moves();
  1956.          FI
  1957.          CLEAR_WINDOW();
  1958.          if((PLAYER.status == ACTIVE) &&
  1959.             (PLAYER.type != NOPLAYER) && ( !player_still_in_game() )) {
  1960.             PLAYER.status = CRUSHED;
  1961.             sprintf( outbuf, "Player %ld has been crushed.", player );
  1962.             (void)rtEZRequestTags
  1963.               ( outbuf,"Hooray!",NULL,NULL,RT_DEFAULT,TAG_END);
  1964.             end_of_player(player);
  1965.          FI
  1966.       OD
  1967.       if (game_overP()) {
  1968.        for( i=1; i<9; i++ ) {
  1969.          if((roster[i].status == ACTIVE)&&(roster[i].type != NOPLAYER)) {
  1970.         sprintf( outbuf, "Player %ld has won the game!", i );
  1971.         (void)rtEZRequestTags
  1972.             ( outbuf,"Congratulations!",NULL,NULL,RT_DEFAULT,
  1973.          TAG_END);
  1974.          }
  1975.        }
  1976.        goto end_of_game;
  1977.       }
  1978. //      if (rtEZRequestTags("Exit test mode?","Yes|No",NULL,NULL,
  1979. //         RTEZ_Flags,EZREQF_CENTERTEXT,
  1980. //         RT_Window,map_window,
  1981. //         RT_ReqPos,REQPOS_CENTERWIN,
  1982. //         RT_LockWindow, TRUE,
  1983. //         TAG_END )) goto end_of_game;
  1984.       if( !human_still_in_game() ) {
  1985.      /* Let the humans know they have been eliminated */
  1986.      (void)rtEZRequestTags
  1987.          ( "All humans have been eliminated. Long live the machines!",
  1988.       "Hooray!",NULL,NULL,RT_DEFAULT,TAG_END);
  1989.      goto end_of_game;
  1990.       }
  1991.       turn++;
  1992.       player = 1;
  1993.    OD
  1994. end_of_game:
  1995.    cleanup_game();
  1996. }
  1997.  
  1998.  
  1999. BOOL game_overP()
  2000. {
  2001.     int i;
  2002.     int in_game = 0;
  2003.  
  2004.     for( i=1; i<9; i++) {
  2005.    if (roster[i].status == ACTIVE && roster[i].type != NOPLAYER) {
  2006.        in_game++;
  2007.    }
  2008.     }
  2009.     if( in_game > 1 ) return FALSE;
  2010.     return TRUE;
  2011. }
  2012.  
  2013.  
  2014. BOOL human_still_in_game()
  2015. {
  2016.     int i;
  2017.     for( i=1; i<9; i++) {
  2018.    if(( roster[i].status == ACTIVE ) &&
  2019.       ( roster[i].type == HUMAN ))
  2020.        return TRUE;
  2021.     }
  2022.     return FALSE;
  2023. }
  2024.  
  2025.  
  2026. BOOL player_still_in_game()
  2027. {
  2028.    struct Unit* unit = (struct Unit*)unit_list.mlh_Head;
  2029.    struct City* metro= (struct City*)city_list.mlh_Head;
  2030.  
  2031.    // If a player owns a single unit, he is still in the game
  2032.    for( ;unit->unode.mln_Succ; unit=(struct Unit*)unit->unode.mln_Succ)
  2033.       if( unit->owner == player ) return TRUE;
  2034.  
  2035.    // Or if a single city is owned, he is also still in the game
  2036.    for( ;metro->cnode.mln_Succ; metro=(struct City*)metro->cnode.mln_Succ)
  2037.       if( metro->owner == player ) return TRUE;
  2038.  
  2039.    return FALSE;
  2040. }
  2041.  
  2042.  
  2043. void cleanup_game()
  2044. {
  2045.    int ctr;
  2046.  
  2047.    // delete the temporary combat report file, if it's there
  2048.    strcpy(foo,prefix);
  2049.    strcat(foo,id_filetag);
  2050.    strcat(foo,".CR");   // CR = Combat Report
  2051.    if (FLength(foo)>=0)
  2052.       DeleteFile(foo);
  2053.  
  2054.    // eradicate each player
  2055.    for (ctr=1; ctr<=8; ctr++) {
  2056.       if (roster[ctr].type!=NOPLAYER) {
  2057.          nuke_list(&roster[ctr].icons);
  2058.          free_map(&roster[ctr].map);
  2059.          if (NONHUMAN(roster[ctr].type))  cleanup_computer ();
  2060.       FI
  2061.       roster[ctr].type = NOPLAYER;
  2062.    OD
  2063.    nuke_list(&city_list);
  2064.    nuke_units(&unit_list);
  2065.    free_map(&t_grid);
  2066.    clear_movebar();
  2067.    // reset PAN
  2068.    strcpy(game_filepath,"ProgDir:Games/");
  2069.    strcpy(game_filename,"Game.IF");
  2070. }
  2071.  
  2072.  
  2073. void play_game()
  2074. {
  2075.    int ctr, retry, abort=FALSE;
  2076.    BPTR file;
  2077.  
  2078.    // deactivate IDCMP event input
  2079.    ModifyIDCMP(map_window,NULL);
  2080.  
  2081.    // having just arrived here from the main program module,
  2082.    // first thing is detach the main menu strip
  2083.    ClearMenuStrip(map_window);
  2084.    // change the title so the user knows where he is
  2085.    SetWindowTitles(map_window,"Game in progress...",(UBYTE *)~0);
  2086.  
  2087.    if (!move_menu_strip)
  2088.       build_move_menu();      // prepare the drop-down menus for use
  2089.    if (!vey_menu_strip)
  2090.       build_survey_menu();
  2091.    if (!prod_menu_strip)
  2092.       build_production_menu();
  2093.  
  2094.    turn = 1;   player = 1;
  2095.    // chose my random filename tag for this game
  2096.    strcpy(foo,"XXXX");
  2097.    for (ctr=0; ctr<4; ctr++)
  2098.       foo[ctr] = (char)('A'+RangeRand(26));
  2099.    strcpy(id_filetag,foo);
  2100.  
  2101.    // create a file for storing combat reports
  2102.    strcpy(foo,prefix);
  2103.    strcat(foo,id_filetag);
  2104.    strcat(foo,".CR");
  2105.    do {
  2106.       retry = FALSE;
  2107.       file = Open(foo,MODE_NEWFILE);
  2108.       if (file)
  2109.          Close(file);
  2110.       else {
  2111.          int choice;
  2112.  
  2113.          sprintf(bar,"Unable to create file %s on T: device.",foo);
  2114.          choice = rtEZRequestTags(
  2115.             bar,"Try Again|Abort Game",NULL,NULL,
  2116.             RT_Window,        map_window,
  2117.             RT_ReqPos,        REQPOS_CENTERWIN,
  2118.             RT_LockWindow,    TRUE,
  2119.             TAG_DONE );
  2120.          abort = (choice==0);
  2121.          retry = !abort;
  2122.       FI
  2123.    } while (retry);
  2124.  
  2125.    control_flag = 0;
  2126.    if (!abort)
  2127.       execute_game_turns();
  2128.  
  2129.    // clear window
  2130.    Move(rast_port,0,0);    ClearScreen(rast_port);
  2131.    zero_scrollers();
  2132.  
  2133.    // reset the title for the main module
  2134.    SetWindowTitles(map_window,"Top Level",(UBYTE *)~0);
  2135.    // remove the gameplay drop-down menus
  2136.    ClearMenuStrip(map_window);
  2137.    // re-attach the main menu strip before returning to the main module
  2138.    ResetMenuStrip(map_window,main_menu_strip);
  2139.    ModifyIDCMP(map_window,IDCMP_MENUPICK);
  2140.  
  2141.    // free the menus that will no longer be needed
  2142.    if (move_menu_strip) {
  2143.       FreeMenus(move_menu_strip);
  2144.       move_menu_strip = NULL;
  2145.    FI
  2146.    if (vey_menu_strip) {
  2147.       FreeMenus(vey_menu_strip);
  2148.       vey_menu_strip = NULL;
  2149.    FI
  2150. }
  2151.  
  2152.  
  2153. void restore_game()
  2154. {     // same as play_game() except no initilization -- game was loaded from disk
  2155.    // deactivate IDCMP event input
  2156.    ModifyIDCMP(map_window,NULL);
  2157.  
  2158.    // having just arrived here from the main program module,
  2159.    // first thing is detach the main menu strip
  2160.    ClearMenuStrip(map_window);
  2161.    // change the title so the user knows where he is
  2162.    SetWindowTitles(map_window,"Game in progress...",(UBYTE *)~0);
  2163.  
  2164.    if (!move_menu_strip)
  2165.       build_move_menu();      // prepare the drop-down menus for use
  2166.    if (!vey_menu_strip)
  2167.       build_survey_menu();
  2168.    if (!prod_menu_strip)
  2169.       build_production_menu();
  2170.  
  2171.    control_flag = GAME_RESTORED;    // special instruction to avoid production phase
  2172.    execute_game_turns();
  2173.  
  2174.    // clear window
  2175.    Move(rast_port,0,0);    ClearScreen(rast_port);
  2176.    zero_scrollers();
  2177.  
  2178.    // reset the title for the main module
  2179.    SetWindowTitles(map_window,"Top Level",(UBYTE *)~0);
  2180.    // remove the gameplay drop-down menus
  2181.    ClearMenuStrip(map_window);
  2182.    // re-attach the main menu strip before returning to the main module
  2183.    ResetMenuStrip(map_window,main_menu_strip);
  2184.    ModifyIDCMP(map_window,IDCMP_MENUPICK);
  2185.  
  2186.    // free the menus
  2187.    if (move_menu_strip) {
  2188.       FreeMenus(move_menu_strip);
  2189.       move_menu_strip = NULL;
  2190.    FI
  2191.    if (vey_menu_strip) {
  2192.       FreeMenus(vey_menu_strip);
  2193.       vey_menu_strip = NULL;
  2194.    FI
  2195. }
  2196.  
  2197. void ToggleAIDataFlag()
  2198. {
  2199.   AIDataFlag++;
  2200.   if( AIDataFlag > AI_DATA_FLAG_LIMIT )  {
  2201.     AIDataFlag = 0;
  2202.   }
  2203.   sprintf( foo, "AI Information at level %ld", AIDataFlag );
  2204.   tell_user2( foo, FALSE, DONK_SOUND );
  2205. }
  2206.  
  2207.  
  2208.  
  2209. // end of listing
  2210.  
  2211.  
  2212.  
  2213.  
  2214.